[日本語Alexa] APLにおけるTouchWrapperコンポーネントによるイベント処理 〜ユーザーの画面タッチ 操作をハンドリングする〜
1 はじめに
CX事業本部の平内(SIN)です。
Amazon AlexaのAPLを構成するTouchWrapperコンポーネントは、1つの子コンポーネントをラップして、タッチイベントを取得します。Alexaで音声以外のイベントを処理するためには、TouchWrapperコンポーネントが必須です。
今回は、TouchWrapperコンポーネントの使い方について、確認してみたいと思います。
2 単一コンポーネント
下記の例は、30vw*20vwのボタンを画面の中央に配置したものです。ちょっと分かりやすいように、TouchWrapperコンポーネントをFrameコンポーネントの中に配置しています。タッチされた際に、SendEventが 発生します。
{ "type": "APL", "version": "1.1", "settings": {}, "theme": "dark", "import": [], "resources": [], "styles": {}, "onMount": [], "graphics": {}, "commands": {}, "layouts": {}, "mainTemplate": { "parameters": [ "payload" ], "items": [ { "type": "Container", "alignItems": "center", "justifyContent": "center", "item": [ { "type": "Frame", "item": { "type": "TouchWrapper", "onPress": [ { "type": "SendEvent", "arguments": [ "button" ] } ], "items": [ { "type": "Text", "width": "30vw", "height": "20vh", "textAlign": "center", "textAlignVertical": "center", "text": "Button" } ] }, "backgroundColor": "#006600", "borderColor": "#00ff00", "borderWidth": "4" } ] } ] } }
3 イベント処理
先のドキュメントが表示された画面で、TouchWrapperコンポーネントをタップすると、Alexa.Presentation.APL.UserEventリクエストがスキルに送られます。
UserEventリクエストのプロパティは以下のとおりです。
- type リクエストタイプ Alexa.Presentation.APL.UserEvent
- token トークン
- arguments SendEventから送られた値
- source 発生元コンポーネント
- components コンポーネントの情報
source.typeで、TouchWrapperコンポーネントからのイベントであることが分かり、argumentsの値を見ることで、対象コンポーネントを判定することができます。
"request": { "type": "Alexa.Presentation.APL.UserEvent", "requestId": "amzn1.echo-api.request.xxxxxxxxx", "timestamp": "2019-07-19T06:49:48Z", "locale": "ja-JP", "arguments": [ "button" ], "components": {}, "source": { "type": "TouchWrapper", "handler": "Press", "id": "", "value": false }, "token": "xxxxxx" }
スキル側でこのイベントを処理するとしたら、下記のようなハンドラになるでしょう。
const TouchEventHandler = { canHandle(h: Alexa.HandlerInput) { const request = h.requestEnvelope.request; return ((request.type === 'Alexa.Presentation.APL.UserEvent' && (request.source.handler === 'Press' || request.source.handler === 'onPress'))); }, handle(h: Alexa.HandlerInput) { const request = h.requestEnvelope.request; if (request.type === 'Alexa.Presentation.APL.UserEvent') { if (request.arguments) { // request.arguments[0]には、"button"が入っている const speechText = `${request.arguments[0]} がタップされました。` return h.responseBuilder .speak(speechText) .withShouldEndSession(true) .getResponse(); } } throw new Error("error"); } };
3 複数コンポーネント
Containerコンポーネント若しくは、Sequenceコンポネントの子としてTouchWrapperコンポーネントを使用すると、結果的に列挙されるデータ数分のコンポーネントのイベントを取得することができます。
下記の例は、30vw*20vwのボタンをContainerコンポーネントの子として配置したものです。ちょっと分かりやすいように、TouchWrapperコンポーネントをFrameコンポーネントの中に配置しています。
タッチされたのが、何番目のコンポーネントかを取得するためには、SendEventのargumentsに "${data.text}" のような変数を指定する必要があります。
const mainDocument = { "type": "APL", "version": "1.1", "settings": {}, "theme": "dark", "import": [], "resources": [], "styles": {}, "onMount": [], "graphics": {}, "commands": {}, "layouts": {}, "mainTemplate": { "parameters": [ "payload" ], "items": [ { "type": "Container", "height": "100vh", "data": [ { "text": "Button001" }, { "text": "Button002" }, { "text": "Button003" }, { "text": "Button004" }, { "text": "Button005" }, { "text": "Button006" }, { "text": "Button007" } ], "alignItems": "center", "items": [ { "type": "Frame", "backgroundColor": "#006600", "borderColor": "#00ff00", "borderWidth": "4", "item": { "type": "TouchWrapper", "onPress": [ { "type": "SendEvent", "arguments": [ "${data.text}" ] } ], "item": { "type": "Text", "width": "30vw", "height": "20vh", "textAlign": "center", "textAlignVertical": "center", "text": "${data.text}" } } } ] } ] } }
5番目のボタンをタップした時にスキルに送られるクリエスとは、次のとおりです。
argumentsに "${data.text}" の内容が入っていることが分かります。ちなみに、source.idで、何番目のコンポーネントかを取得することはできません。
"request": { "type": "Alexa.Presentation.APL.UserEvent", "requestId": "amzn1.echo-api.request.xxxxxxxxx", "timestamp": "2019-07-24T06:14:46Z", "locale": "ja-JP", "arguments": [ "Button005" ], "components": {}, "source": { "type": "TouchWrapper", "handler": "Press", "id": "" }, "token": "token" }
4 最後に
今回は、TouchWrapperコンポーネントについて確認してみました。
Sequenceコンポーネントや、Containerコンポーネントの中で利用することで、リストの選択が簡単に実現できます。
ちなみに、TouchWrapperコンポーネントの中に、Containerコンポーネント等を配置することも可能ですが、argumentsで区別できないため、リスト等の選択には利用できません。